package watcher

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/fatih/color"
	"github.com/fsnotify/fsnotify"
	"gs-demo-ftp-sync/internal/config"
	"gs-demo-ftp-sync/internal/ftp"
)

// SyncAction 同步操作类型
type SyncAction string

const (
	ActionUpload SyncAction = "upload"
	ActionDelete SyncAction = "delete"
)

// SyncTask 同步任务
type SyncTask struct {
	LocalPath  string
	RemotePath string
	Action     SyncAction
	Timestamp  time.Time
}

// Watcher 文件监控器
type Watcher struct {
	config    *config.Config
	ftpClient *ftp.Client
	watcher   *fsnotify.Watcher
	
	// 同步队列
	syncQueue map[string]*SyncTask
	queueMux  sync.RWMutex
	
	// 控制
	ctx    context.Context
	cancel context.CancelFunc
	wg     sync.WaitGroup
}

// NewWatcher 创建新的文件监控器
func NewWatcher(cfg *config.Config, ftpClient *ftp.Client) *Watcher {
	ctx, cancel := context.WithCancel(context.Background())
	
	return &Watcher{
		config:    cfg,
		ftpClient: ftpClient,
		syncQueue: make(map[string]*SyncTask),
		ctx:       ctx,
		cancel:    cancel,
	}
}

// Start 启动文件监控
func (w *Watcher) Start() error {
	color.Blue("启动文件监控...")

	// 创建文件系统监控器
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		return fmt.Errorf("创建文件监控器失败: %v", err)
	}
	w.watcher = watcher

	// 监控目录
	watchPaths := []struct {
		local  string
		remote string
		name   string
	}{
		{
			local:  w.config.LocalAddonPath,
			remote: w.config.RemoteAddonPath,
			name:   "ThinkPHP插件",
		},
		{
			local:  w.config.LocalAssetsPath,
			remote: w.config.RemoteAssetsPath,
			name:   "前端资源",
		},
	}

	for _, path := range watchPaths {
		if w.pathExists(path.local) {
			if err := w.watchDirectory(path.local, path.remote, path.name); err != nil {
				color.Yellow("警告: 无法监控 %s 目录 - %s: %v", path.name, path.local, err)
			}
		} else {
			color.Yellow("警告: %s目录不存在 - %s", path.name, path.local)
		}
	}

	// 启动监控协程
	w.wg.Add(2)
	go w.watchEvents()
	go w.processSyncQueue()

	color.Green("✓ 文件监控已启动，等待文件变化...")
	return nil
}

// Stop 停止文件监控
func (w *Watcher) Stop() {
	color.Blue("停止文件监控...")
	
	w.cancel()
	
	if w.watcher != nil {
		w.watcher.Close()
	}
	
	w.wg.Wait()
	color.Green("✓ 文件监控已停止")
}

// watchDirectory 监控目录
func (w *Watcher) watchDirectory(localPath, remotePath, displayName string) error {
	// 添加目录到监控
	err := w.watcher.Add(localPath)
	if err != nil {
		return err
	}

	// 递归添加子目录
	err = filepath.Walk(localPath, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		
		if info.IsDir() {
			return w.watcher.Add(path)
		}
		return nil
	})

	if err != nil {
		return err
	}

	color.Green("✓ 开始监控 %s: %s", displayName, localPath)
	return nil
}

// watchEvents 监控文件事件
func (w *Watcher) watchEvents() {
	defer w.wg.Done()

	for {
		select {
		case event, ok := <-w.watcher.Events:
			if !ok {
				return
			}
			w.handleEvent(event)

		case err, ok := <-w.watcher.Errors:
			if !ok {
				return
			}
			color.Red("监控错误: %v", err)

		case <-w.ctx.Done():
			return
		}
	}
}

// handleEvent 处理文件事件
func (w *Watcher) handleEvent(event fsnotify.Event) {
	// 忽略临时文件和目录
	if w.shouldIgnore(event.Name) {
		return
	}

	// 确定本地和远程路径
	localPath, remotePath, displayName := w.getPaths(event.Name)
	if localPath == "" {
		return
	}

	relativePath := w.getRelativePath(event.Name, localPath)

	switch {
	case event.Op&fsnotify.Create == fsnotify.Create:
		// 检查是文件还是目录
		if info, err := os.Stat(event.Name); err == nil {
			if info.IsDir() {
				color.Cyan("[%s] 新增目录: %s", displayName, relativePath)
				// 添加新目录到监控
				w.watcher.Add(event.Name)
			} else {
				color.Cyan("[%s] 新增文件: %s", displayName, relativePath)
				w.queueSync(event.Name, remotePath+"/"+relativePath, ActionUpload)
			}
		}

	case event.Op&fsnotify.Write == fsnotify.Write:
		// 只处理文件写入
		if info, err := os.Stat(event.Name); err == nil && !info.IsDir() {
			color.Cyan("[%s] 修改文件: %s", displayName, relativePath)
			w.queueSync(event.Name, remotePath+"/"+relativePath, ActionUpload)
		}

	case event.Op&fsnotify.Remove == fsnotify.Remove:
		color.Yellow("[%s] 删除: %s", displayName, relativePath)
		w.queueSync("", remotePath+"/"+relativePath, ActionDelete)

	case event.Op&fsnotify.Rename == fsnotify.Rename:
		color.Yellow("[%s] 重命名: %s", displayName, relativePath)
		w.queueSync("", remotePath+"/"+relativePath, ActionDelete)
	}
}

// queueSync 将同步任务加入队列
func (w *Watcher) queueSync(localPath, remotePath string, action SyncAction) {
	w.queueMux.Lock()
	defer w.queueMux.Unlock()

	key := remotePath
	
	// 如果任务已存在，更新它
	if existing, exists := w.syncQueue[key]; exists {
		existing.LocalPath = localPath
		existing.Action = action
		existing.Timestamp = time.Now()
	} else {
		w.syncQueue[key] = &SyncTask{
			LocalPath:  localPath,
			RemotePath: remotePath,
			Action:     action,
			Timestamp:  time.Now(),
		}
	}
}

// processSyncQueue 处理同步队列
func (w *Watcher) processSyncQueue() {
	defer w.wg.Done()

	ticker := time.NewTicker(time.Duration(w.config.SyncDelay) * time.Millisecond)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			w.processPendingTasks()

		case <-w.ctx.Done():
			// 处理剩余任务
			w.processPendingTasks()
			return
		}
	}
}

// processPendingTasks 处理待处理的任务
func (w *Watcher) processPendingTasks() {
	w.queueMux.Lock()
	tasks := make([]*SyncTask, 0, len(w.syncQueue))
	for _, task := range w.syncQueue {
		tasks = append(tasks, task)
	}
	// 清空队列
	w.syncQueue = make(map[string]*SyncTask)
	w.queueMux.Unlock()

	// 处理任务
	for _, task := range tasks {
		w.executeSync(task)
	}
}

// executeSync 执行同步任务
func (w *Watcher) executeSync(task *SyncTask) {
	var err error

	switch task.Action {
	case ActionUpload:
		if task.LocalPath != "" {
			err = w.ftpClient.UploadFile(task.LocalPath, task.RemotePath)
		}
	case ActionDelete:
		err = w.ftpClient.DeleteFile(task.RemotePath)
	}

	if err != nil {
		color.Red("同步失败 [%s]: %s - %v", task.Action, task.RemotePath, err)
		
		// 如果是上传失败，可以重试
		if task.Action == ActionUpload {
			// 延迟重试
			go func() {
				time.Sleep(10 * time.Second)
				w.queueSync(task.LocalPath, task.RemotePath, task.Action)
			}()
		}
	}
}

// getPaths 根据文件路径确定本地和远程路径
func (w *Watcher) getPaths(filePath string) (localPath, remotePath, displayName string) {
	if strings.HasPrefix(filePath, w.config.LocalAddonPath) {
		return w.config.LocalAddonPath, w.config.RemoteAddonPath, "ThinkPHP插件"
	}
	if strings.HasPrefix(filePath, w.config.LocalAssetsPath) {
		return w.config.LocalAssetsPath, w.config.RemoteAssetsPath, "前端资源"
	}
	return "", "", ""
}

// getRelativePath 获取相对路径
func (w *Watcher) getRelativePath(filePath, basePath string) string {
	rel, err := filepath.Rel(basePath, filePath)
	if err != nil {
		return filePath
	}
	return strings.ReplaceAll(rel, "\\", "/")
}

// shouldIgnore 检查是否应该忽略文件
func (w *Watcher) shouldIgnore(filePath string) bool {
	fileName := filepath.Base(filePath)
	
	// 检查排除模式
	for _, pattern := range w.config.ExcludePatterns {
		if matched, _ := filepath.Match(pattern, fileName); matched {
			return true
		}
		if strings.Contains(fileName, pattern) {
			return true
		}
	}

	// 忽略临时文件
	tempExtensions := []string{".tmp", ".temp", ".swp", ".swx", "~"}
	for _, ext := range tempExtensions {
		if strings.HasSuffix(fileName, ext) {
			return true
		}
	}

	return false
}

// pathExists 检查路径是否存在
func (w *Watcher) pathExists(filePath string) bool {
	_, err := os.Stat(filePath)
	return !os.IsNotExist(err)
}
